/*
 * ModelViewer.cpp
 *
 *  Created on: 09/05/2013
 *      Author: gabriel
 */

#include "ModelViewer.hpp"
#include "stdio.h"
#include "GLError.h"




#define VALS_PER_VERT 3
#define VALS_PER_TEX 2
#define VALS_PER_COLOUR 4
#define VALS_PER_INDEX 4		// The number of vertices in our square, used for array size and in rendering
#define CUBE_NUM_SQUARES 6      // number of squares in a cube
#define CUBE_NUM_TRIANGLES 12   // number of triangles in a cube
#define CUBE_NUM_FACES 6
#define TRIANGLE_PER_FACE 2
#define CUBE_NUM_VERTICES 8     // number of vertices in a cube`



ModelViewer::ModelViewer() {
	this->vertices = new Vertex();
	this->vertexVboId = -1;
	this->iCounts = new int();
	this->vertexCount= -1;
	this->textureVboId = new unsigned int();
	this->shiny = new float();

}
ModelViewer::~ModelViewer() {
}


bool ModelViewer::load(std::string fileName, Shader *program) {
	glUseProgram(program->programHandle);

	objLoader *objData = new objLoader();
	objData->load( fileName.c_str() );


	int vertLoc = glGetAttribLocation(program->programHandle, "a_vertex");
	int normalLoc = glGetAttribLocation(program->programHandle, "a_normal");
	int texLoc = glGetAttribLocation(program->programHandle,"a_tex_coord");
	int ambientLoc 	 = glGetAttribLocation(program->programHandle, "a_colour_ambient");
	int diffuseLoc 	 = glGetAttribLocation(program->programHandle, "a_colour_diffuse");
	int specularLoc  = glGetAttribLocation(program->programHandle, "a_colour_specular");
	int emitLoc 	 = glGetAttribLocation(program->programHandle, "a_colour_emissive");



	vertexCount  = objData->vertexCount;
	faceCount 	= objData->faceCount;
	textureCount = objData->textureCount;
	materialCount = objData->materialCount;




	shiny = new float[materialCount];
	indexVboId = new unsigned int[materialCount];
	textureVboId = new unsigned int[materialCount];
	iCounts = new int[materialCount];



	vertices = new Vertex[vertexCount];
	for (int i = 0; i < vertexCount; i++) {
		for (int j = 0 ; j < 3; j++) {
			vertices[i].normal[j] = 0;
			//vertices[i].coords[j] = objData->vertexList[i]->e[j];

		}
		vertices[i].coords[0] = objData->vertexList[i]->e[0];
		vertices[i].coords[1] = objData->vertexList[i]->e[1];
		vertices[i].coords[2] = objData->vertexList[i]->e[2];

		vertices[i].texCoords[0] = 0;
		vertices[i].texCoords[1] = 0;

	}





	for (int k = 0; k < materialCount; k++){

		int iCount = 0;
		for(int l = 0; l < objData->faceCount; l++){
			if(objData->faceList[l]->material_index == k)
				iCount +=3;
		}


		iCounts[k] = iCount;
		unsigned int *indices = new unsigned int[iCount];
		int indexCount = 0;
		for(int i = 0; i < objData->faceCount; i++ ){
			obj_face *face= objData->faceList[i];
			if(face->material_index == k){
				for(int  j = 0; j < 3; j++){
					unsigned int vertexIndex = face->vertex_index[j];
					indices[indexCount++] = vertexIndex;

					vertices[vertexIndex].normal[0] = objData->normalList[face->normal_index[j]]->e[0];
					vertices[vertexIndex].normal[1] = objData->normalList[face->normal_index[j]]->e[1];
					vertices[vertexIndex].normal[2] = objData->normalList[face->normal_index[j]]->e[2];

					if(face->texture_index[j]>=0){

						vertices[vertexIndex].texCoords[0] =  objData->textureList[face->texture_index[j]]->e[0];
						vertices[vertexIndex].texCoords[1] =  objData->textureList[face->texture_index[j]]->e[1];



					}

					for(int n = 0; n<4; n++){
						vertices[vertexIndex].ambient[n] = objData->materialList[k]->amb[n];
						vertices[vertexIndex].diffuse[n] = objData->materialList[k]->diff[n];
						vertices[vertexIndex].specular[n] = objData->materialList[k]->spec[n];
						vertices[vertexIndex].emit[n] = objData->materialList[k]->emit[n];
					}
				}
			}
			shiny[k] = objData->materialList[k]->shiny;
		}



		if (strcmp(objData->materialList[k]->texture_filename,"") !=0 ){
			textureVboId[k] = loadTexture(objData->materialList[k]->texture_filename);

		}else{
			textureVboId[k] = -1;


		}




		glGenBuffers(1, indexVboId+k);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVboId[k]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*iCount, indices, GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);



		delete[]indices;
	}






	glGenBuffers(1, &vertexVboId);
	glBindBuffer(GL_ARRAY_BUFFER, vertexVboId);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertexCount, vertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);




	glGenVertexArrays(1, &vaoHandle);
	glBindVertexArray(vaoHandle);

	glBindBuffer( GL_ARRAY_BUFFER, vertexVboId );

	glEnableVertexAttribArray(vertLoc);
	glVertexAttribPointer(vertLoc, VALS_PER_VERT, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, coords));

	glEnableVertexAttribArray(normalLoc);
	glVertexAttribPointer(normalLoc, VALS_PER_VERT, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));

	glEnableVertexAttribArray(texLoc);
	glVertexAttribPointer(texLoc, VALS_PER_TEX, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoords));

	if(ambientLoc > 0){
		glEnableVertexAttribArray(ambientLoc);
		glVertexAttribPointer(ambientLoc, VALS_PER_COLOUR, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, ambient));
	}

	if(diffuseLoc > 0){
		glEnableVertexAttribArray(diffuseLoc);
		glVertexAttribPointer(diffuseLoc, VALS_PER_COLOUR, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, diffuse));
	}

	if(specularLoc > 0){
		glEnableVertexAttribArray(specularLoc);
		glVertexAttribPointer(specularLoc, VALS_PER_COLOUR, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, specular));
	}

	if(emitLoc > 0){
		glEnableVertexAttribArray(emitLoc);
		glVertexAttribPointer(emitLoc, VALS_PER_COLOUR, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, emit));
	}


	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);



	return true;
}

unsigned int ModelViewer::loadTexture(std::string filename){

	TgaImage img(filename.c_str());


	unsigned int texHandle;
	glGenTextures(1, &texHandle);

	glBindTexture(GL_TEXTURE_2D, texHandle);

	glTexImage2D(GL_TEXTURE_2D,
			0,
			img.dataType,
			img.imgWidth,
			img.imgHeight,
			0,
			img.format,
			GL_UNSIGNED_BYTE,
			img.imgData);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);




	return texHandle;
}

void ModelViewer::render(Shader *program){
	glUseProgram(program->programHandle);
	glBindVertexArray(vaoHandle);
	int shinyUniformHandle = program->getUniformHandle("a_shiny");


	for(int i = 0; i< materialCount; i++){

		if(shinyUniformHandle >0 )
			glUniform1f(shinyUniformHandle, shiny[i]);
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, indexVboId[i]);

		if(textureVboId[i] >=0){
			glEnable(GL_TEXTURE_2D);
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, textureVboId[i]);
			int textureUniformHandle = program->getUniformHandle("texMap");
			glUniform1i(textureUniformHandle, 0);
		}



		glDrawElements(GL_TRIANGLES, iCounts[i], GL_UNSIGNED_INT, 0);


	}

	glBindTexture(GL_TEXTURE_2D, 0);
	glDisable(GL_TEXTURE_2D);
	glBindVertexArray(0);

}



